package Entities;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Hashtable;

import Statistics.*;
import DisplayText.*;
import Effects.Effect;
import Effects.Buffs.Buff;
import Effects.Debuffs.Debuff;


public abstract class RPGEntity 
{

	/* Vars */
	private int entityID;
	private Hashtable<String, Buff> buffs;
	private Hashtable<String, Debuff> debuffs;
	private Statistics statistics;
	private ArrayList<Skill> abilities;
	
	
	/**
	 * Constructor
	 */
	public RPGEntity(int incID, int primaryStat)
	{
		//Will need method to get IDs
		this.entityID = incID;
		this.buffs = new Hashtable<String, Buff>();
		this.debuffs = new Hashtable<String, Debuff>();
		this.statistics = new Statistics(primaryStat);
		this.abilities = new ArrayList<Skill>();
	}
	
	/**
	 * Attacks the target
	 * @param target
	 */
	public void attack(RPGEntity target)
	{
		//Logic containing attack information.
	}
	
	/**
	 * Adds the effect to the entity.
	 * @param which Effect to add.
	 */
	public void addEffect(Effect which)
	{
		//Making sure the buff hasn't already been assigned to somebody.
		if(!which.hasHost())
		{
			which.applyEffect();
			which.setHost(this);
			
			if(which instanceof Buff)
			{
				buffs.put(which.getKey(), (Buff) which);
			}
			else if(which instanceof Debuff)
			{
				debuffs.put(which.getKey(), (Debuff) which);
			}
		}
	}
	
	/**
	 * Removes the Effect.
	 * @param which effect to remove
	 */
	public void removeEffect(Effect which)
	{		
		if(which instanceof Buff)
		{
			if(buffs.containsKey(which.getKey()))
			{
				which.removeEffect();
				buffs.remove(which);
			}
		}
		else if(which instanceof Debuff)
		{
			if(debuffs.containsKey(which.getKey()))
			{
				which.removeEffect();
				debuffs.remove(which);
			}
		}
	}
	
	/**
	 * Checks to see if this entity has the effect.
	 * @param which
	 * @return 
	 */
	public Effect hasEffect(Effect which)
	{
		Effect result = null;

		if(which instanceof Buff)
		{
			if(buffs.contains(which))
			{
				result = buffs.get(which.getKey());
			}
		}
		else if(which instanceof Debuff)
		{
			if(debuffs.contains(which))
			{
				result = debuffs.get(which.getKey());
			}
		}
		
		return result;
	}
	
	/**
	 * Hurts the rpg entity
	 * @param dmgAmt how much damage
	 * *NOTE* This simply deals the damage specified,
	 * 			does not do damage calculations.
	 */
	public void damage(int dmgAmt)
	{
		int currentHP = (int)this.statistics.getStatValue(Statistics.STAT_HP_CURRENT);
		currentHP -= dmgAmt;
		
		if(currentHP < 0)
		{
			currentHP = 0;
		}
		
		this.statistics.setStat(Statistics.STAT_HP_CURRENT, currentHP);
		DisplayText.displayCombatText(String.valueOf(dmgAmt), Color.RED);
	}

	/**
	 * Checks to see if this entity is dead.
	 * @return true if it's hp is 0, false otherwise
	 */
	public boolean checkIfDead()
	{
		boolean result = false;
		
		if((int)this.statistics.getStatValue(Statistics.STAT_HP_CURRENT) <= 0)
		{
			result = true;
		}
		
		return result;
	}
	
	/**
	 * Heals the RPGEntity
	 * @param healAmt Amount to heal him by
	 */
	public void heal(int healAmt)
	{
		int currentHP = (int)this.statistics.getStatValue(Statistics.STAT_HP_CURRENT);
		currentHP += healAmt;
		int maxHP = (int)this.statistics.getStatValue(Statistics.STAT_HP_MAX);
		
		if(currentHP > maxHP)
		{
			currentHP = maxHP;
		}
		
		this.statistics.setStat(Statistics.STAT_HP_CURRENT, currentHP);
		DisplayText.displayCombatText(String.valueOf(healAmt), Color.GREEN);
	}
	
	/**
	 * Kills this entity.
	 */
	public abstract void die();
	
	/**
	 * Sets the specified stat to the number.
	 * @param stat Statistics.STAT_*
	 * @param changeAmt new value to set it to.
	 */
	public void setStat(int stat, double changeAmt)
	{
		this.statistics.setStat(stat, changeAmt);
	}
	
	/**
	 * Increases the specified stat.
	 * @param stat Statistics.STAT_
	 * @param changeAmt Amount to increase it by.
	 */
	public void increaseStat(int stat, double changeAmt)
	{
		this.statistics.increaseStat(stat, changeAmt);
	}
	
	/**
	 * Decreases the specified stat.
	 * @param stat Statistics.STAT_
	 * @param changeAmt Amount to decrease it by.
	 */
	public void decreaseStat(int stat, double changeAmt)
	{
		this.statistics.decreaseStat(stat, changeAmt);
	}
	
	/**
	 * Gets the stat value of the specified stat
	 * @param stat Statistics.STAT_*
	 * @return Value contained within the stat.
	 */
	public double getStatValue(int stat)
	{
		return this.statistics.getStatValue(stat);		
	}

	/**
	 * Gets this entities level.
	 * @return this entities level
	 */
	public int getLevel()
	{
		return (int)this.statistics.getStatValue(Statistics.STAT_LEVEL);	
	}

	/**
	 * Awards the entity with the amount of exp
	 * provided.
	 * @param howMuch exp to give
	 */
	public void awardExp(int howMuch)
	{
		this.statistics.increaseStat(Statistics.STAT_EXP_CURRENT, howMuch);
		
		//Checking to see if he leveled
		if(statistics.getStatValue(Statistics.STAT_EXP_CURRENT) >= 
			statistics.getStatValue(Statistics.STAT_EXP_MAX))
		{
			levelUp();
		}
	}
	
	/**
	 * Increases this entities level by 1. 
	 */
	public void levelUp()
	{
		this.statistics.increaseStat(Statistics.STAT_LEVEL, 1);
		doLevelUp();
	}
	
	/**
	 * Applies all the stats and new abilites this entity should
	 * get after leveling.
	 */
	protected abstract void doLevelUp();
	
}
